package com.nulldev.util.processAPI;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.data.FinalBypass;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.logging.LoggerLook;
import com.nulldev.util.osAPI.OS;
import com.nulldev.util.osAPI.Packages;
import com.nulldev.util.osAPI.OS.OperatingSystem;
import com.nulldev.util.processAPI.Process.ProcessType;
import com.nulldev.util.processAPI.Processes.ProcessUtilRead;

public class ProcessManager {
	public static boolean reduceCPUCalls;
	private static final List<Process> PROCESSES = new ArrayList<Process>();
	private static boolean isParsing;
	private static final Logger log = LoggerFactory.getLogger(LoggerLook.v2, true);
	public static final Process BLANK = new Process(-1, "N/A", ProcessType.UNKNOWN);

	public static List<Process> parse() {
		if (isParsing) {
			log.error("parse() -> Already parsing!");
			return PROCESSES;
		}
		isParsing = true;
		if (OS.get() == OperatingSystem.Windows) {
			try {
				final ArrayList<Process> process = new ArrayList<Process>();

				final String result = Processes.execCmd("tasklist /v /FO:CSV");
				final String[] lines = StringsUtil.byLineSepeartor(result);
				for (int i = 1; i < lines.length; i++) {
					final String line = lines[i];
					// final String[] data = line.split(",");
					final String[] data = StringsUtil.safeSplit(line).split("__sp__");

					ProcessType ty = ProcessType.UNKNOWN;
					final boolean isConsole = StringsUtil.isEqualCM(data, 2, "Console");
					final boolean isService = StringsUtil.isEqualCM(data, 2, "Services");
					if (isConsole) {
						ty = ProcessType.Console;
					} else if (isService) {
						ty = ProcessType.Service;
					} else {
						ty = ProcessType.Process;
					}

					final Process p = new Process(Long.parseLong(data[1].replaceAll("\"", "")), data[0].replaceAll("\"", ""), ty);
					p.setTitle(data[data.length - 1].replaceAll("\"", ""));
					process.add(p);
				}

				PROCESSES.clear();
				PROCESSES.addAll(process);
				isParsing = false;
				return process;
			} catch (Exception e) {
				JVM.errorln(e);
			}
		} else if (OS.get() == OperatingSystem.Linux) {
			try {
				final ArrayList<Process> process = new ArrayList<Process>();

				final String result = Processes.execCmd("ps aux").replaceAll("Program Files", "%pf");
				final String[] data = StringsUtil.limitSpaces(result, 1).split(JVM.lineSeperator());
				data[0] = "";
				for (final String lineX : data) {
					final String[] line = lineX.split(" ");
					final String proc = ArrayUtils.StringArrayUtils.at(line, 10, "N/A").replaceAll(Pattern.quote("%pf"), "");
					final Process pro = new Process(MathUtil.safeParseList(line, 1, -1), proc, ProcessType.Process);
					pro.setMemoryUsage(MathUtil.safeParseList(line, 4, -1));
					pro.setArgs(ArrayUtils.StringArrayUtils.addFrom(line, 11));
					process.add(pro);
				}
				PROCESSES.clear();
				PROCESSES.addAll(process);
				boolean fetched = true;
				if (Packages.isPackage("wmctrl")) {
					final String pre_out = Processes.execCmd("wmctrl -lp -i");
					final String out = StringsUtil.limitSpaces(pre_out, 1);
					final String[] dta = out.split(JVM.lineSeperator());
					for (final String lt : dta) {
						final String[] vals = lt.split(" ");
						final long pid = MathUtil.safeParseList(vals, 2, -1);
						if (pid > 0) {
							final Process px = find(pid);
							px.setTitle(ArrayUtils.StringArrayUtils.addFrom(vals, 4));
						}
					}
					fetched = true;
				}
				if (!reduceCPUCalls || !fetched) {
					if (Packages.isPackage("xprop")) {
						try {
							final ProcessBuilder cmd = new ProcessBuilder("/bin/sh", "-c", "xprop -root | grep \"_NET_CLIENT_LIST_STACKING(WINDOW)\"");
							final FinalBypass<String> pre_out = new FinalBypass<String>("");
							Processes.ProcessUtilActive.execActiveCmd_NoQueue_UseProcessBuilder(cmd, new ProcessUtilRead() {

								@Override
								public void onRead(String input) {
									pre_out.setObject(pre_out.getObject() + input);
								}
							});
							final String p_f = pre_out.getObject().replaceAll(Pattern.quote("_NET_CLIENT_LIST_STACKING(WINDOW): window id # "), "");
							final String[] vals = p_f.split(", ");
							for (final String val : vals) {
								final ProcessBuilder cmda = new ProcessBuilder("/bin/sh", "-c", "xprop -id " + val + " | grep _NET_WM_PID");
								final FinalBypass<String> p = new FinalBypass<String>("");
								Processes.ProcessUtilActive.execActiveCmd_NoQueue_UseProcessBuilder(cmda, new ProcessUtilRead() {

									@Override
									public void onRead(String input) {
										p.setObject(p.getObject() + input);
									}
								});
								final String PID = p.getObject().replaceAll(Pattern.quote("_NET_WM_PID(CARDINAL) = "), "");
								final ProcessBuilder cmdb = new ProcessBuilder("/bin/sh", "-c", "xprop -id " + val + " | grep \"_NET_WM_NAME(UTF8_STRING)\"");
								final FinalBypass<String> p1 = new FinalBypass<String>("");
								Processes.ProcessUtilActive.execActiveCmd_NoQueue_UseProcessBuilder(cmdb, new ProcessUtilRead() {

									@Override
									public void onRead(String input) {
										p1.setObject(p1.getObject() + input);
									}
								});
								final String title = p1.getObject().replaceAll(Pattern.quote("_NET_WM_NAME(UTF8_STRING) = "), "");
								if (PID != null && PID.length() > 0 && !PID.equals("")) {
									final Process px = find(Long.parseLong(PID));
									if (!px.isBlank()) {
										try {
											px.setTitle(title.substring(1, title.length() - 1));
										} catch (Exception e) {
											px.setTitle(title);
										}
									}
								}
							}
						} catch (Exception e) {
							JVM.errorln(e);
						}
					}
				}
				isParsing = false;
				return process;
			} catch (Exception e) {
				JVM.errorln(e);
			}
		}
		log.error("parse() -> Unsupported OS!");
		isParsing = false;
		return new ArrayList<Process>();
	}

	public static Process find(final String name) {
		if (PROCESSES.isEmpty()) {
			parse();
		}
		for (final Process process : PROCESSES) {
			if (process.getExecutable().equals(name)) {
				return process;
			}
		}
		return ProcessManager.BLANK;
	}

	public static Process findE(final String name) {
		if (PROCESSES.isEmpty()) {
			parse();
		}
		for (final Process process : PROCESSES) {
			if (process.getExecutable().equals(name) || process.getExecutable().endsWith(name)) {
				return process;
			}
		}
		return ProcessManager.BLANK;
	}

	public static Process find(final long pid) {
		if (PROCESSES.isEmpty()) {
			parse();
		}
		for (final Process process : PROCESSES) {
			if (process.pid() == pid) {
				return process;
			}
		}
		return ProcessManager.BLANK;
	}

	public static void main(String[] args) {
		ArrayUtils.printLln(parse());
	}
}
